package com.qfedu.aop;

import com.qfedu.log.pojo.Log;
import com.qfedu.log.service.LogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

//这个注解表示当前这个类是一个切面类
//切面类必须要放入IOC容器才能使得切面生效
@Aspect
@Component //这个注解表示当前类是一个组件，需要创建对象放入IOC容器中
public class LogAspect {

    @Autowired
    private LogService logService;

    @Pointcut("execution(* com.qfedu.*.controller..*(..))")
    public void pointcut(){}

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //获取连接点的签名信息
        Signature signature = pjp.getSignature();
        //因为spring的连接点类型只有方法，所以连接点的签名信息一定是一个方法的签名信息
        MethodSignature ms = (MethodSignature) signature;
        Method method = ms.getMethod();
        //获取方法所在的类
        Class<?> clazz = method.getDeclaringClass();
        String url = "";
        RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
        if(requestMapping != null){
            String[] urls = requestMapping.value();
           if(urls.length == 1){
               url = urls[0];
           }
        }
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        if(getMapping != null){
            String[] urls = getMapping.value();
            if(urls.length == 1){
                url += urls[0];
            }
        }
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        if(postMapping != null){
            String[] urls = postMapping.value();
            if(urls.length == 1){
                url += urls[0];
            }
        }

        PutMapping putMapping = method.getAnnotation(PutMapping.class);
        if(putMapping != null){
            String[] urls = putMapping.value();
            if(urls.length == 1){
                url += urls[0];
            }
        }
        DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
        if(deleteMapping != null){
            String[] urls = deleteMapping.value();
            if(urls.length == 1){
                url += urls[0];
            }
        }
        if(StringUtils.hasLength(url)){
            Object[] args = pjp.getArgs();
            Object[] parameters = Arrays.stream(args).filter(arg -> !(arg instanceof HttpSession)
                    && !(arg instanceof HttpServletRequest)
                    && !(arg instanceof HttpServletResponse)
                    && !(arg instanceof MultipartFile)
                    && !(arg instanceof ModelMap)).toArray();
            String parameter = Arrays.toString(parameters);
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) attributes;
            HttpServletRequest request = sra.getRequest();
            HttpSession session = request.getSession();
            String username = (String) session.getAttribute("username");
            int error = 0;
            Object result = null;
            Throwable t = null;
            try {
                result = pjp.proceed();
            } catch (Throwable e) {
                error = 1;
                t = e;
            }
            if(!"/login".equals(url)){//操作日志不记录登录操作
                Log log = new Log();
                log.setUrl(url);
                log.setParameter(parameter);
                log.setOperationTime(new Date());
                log.setUser(username);
                log.setError(error);
                logService.save(log);
            }
            //验证是否出现了异常，如果没有出现异常，那么就应该给出一个返回结果
            if(t == null) return result;
            //如果出现了异常，那么直接将异常抛出
            else throw t;
        } else {
            //其余情况说明这个方法不是处理请求的方法，不需要记录日志
            return pjp.proceed();
        }
    }
}
